#import "WebSocketChannel.h"
#import <Foundation/Foundation.h>

#import "WebRTC/RTCLogging.h"
#import "SRWebSocket.h"
#import "JanusTransaction.h"
#import "JanusHandle.h"
#import "BotSi.h"
typedef NS_ENUM(NSInteger, ARDSignalingChannelState) {
    kARDSignalingChannelStateClosed,
    kARDSignalingChannelStateOpen,
    kARDSignalingChannelStateCreate,
    kARDSignalingChannelStateAttach,
    kARDSignalingChannelStateJoin,
    kARDSignalingChannelStateOffer,
    kARDSignalingChannelStateError
    
};

static NSString const *kJanus = @"janus";
static NSString const *kJanusData = @"data";


@interface WebSocketChannel () <SRWebSocketDelegate>
{
    NSURL *_url;
    SRWebSocket *_socket;
    NSNumber *sessionId;
    NSTimer *keepAliveTimer;
    NSMutableDictionary *transDict;
    NSMutableDictionary *handleDict;
    NSMutableDictionary *feedDict;
}
@property(nonatomic, readonly) ARDSignalingChannelState state;

@end

@implementation WebSocketChannel


@synthesize state = _state;


- (instancetype)initWithURL:(NSURL *)url {
    if (self = [super init]) {
        
        _url = url;
        transDict = [[NSMutableDictionary alloc] init];
        handleDict = [[NSMutableDictionary alloc]init];
        feedDict = [[NSMutableDictionary alloc]init];
        NSArray<NSString *> *protocols = [NSArray arrayWithObject:@"janus-protocol"];
        _socket = [[SRWebSocket alloc] initWithURL:url protocols:(NSArray *)protocols];
        _socket.delegate = self;
        keepAliveTimer = [NSTimer scheduledTimerWithTimeInterval:30.0 target:self selector:@selector(keepAlive) userInfo:nil repeats:YES];
        
        
        RTCLog(@"Opening WebSocket.");
        [_socket open];
        
        
    }
    return self;
}

- (void)dealloc
{
    [self disconnect];
}

- (void)setState:(ARDSignalingChannelState)state {
    if (_state == state) {
        return;
    }
    _state = state;
}

- (void)disconnect
{
    if (_state == kARDSignalingChannelStateClosed ||
        _state == kARDSignalingChannelStateError) {
        return;
    }
    [_socket close];
    _socket=nil;
    RTCLog(@"C->WSS DELETE close");
}

#pragma mark - SRWebSocketDelegate

- (void)webSocketDidOpen:(SRWebSocket *)webSocket {
    RTCLog(@"WebSocket connection opened.");
    NSLog(@"====kARDSignalingChannelStateOpen");
    self.state = kARDSignalingChannelStateOpen;
    [self createSession];
}

- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message {
    
    NSData *messageData = [message dataUsingEncoding:NSUTF8StringEncoding];
    id jsonObject = [NSJSONSerialization JSONObjectWithData:messageData options:0 error:nil];
    if (![jsonObject isKindOfClass:[NSDictionary class]]) {
        NSLog(@"Unexpected message: %@", jsonObject);
        return;
    }

    NSDictionary *wssMessage = jsonObject;
//    NSLog(@"Unexpected message: %@", wssMessage);
    NSString *janus = wssMessage[kJanus];
    if ([janus isEqualToString:@"success"]) {
        
        NSString *transaction = wssMessage[@"transaction"];
        
        JanusTransaction *jt = transDict[transaction];
        if (jt.success != nil) {
            jt.success(wssMessage);
        }
        [transDict removeObjectForKey:transaction];
    }
    else if ([janus isEqualToString:@"event"]) {
        
        NSDictionary *plugin = wssMessage[@"plugindata"][@"data"];
        NSString *jsep = wssMessage[@"jsep"];
        //                 NSLog(@"jsep:%@",jsep);
        JanusHandle *handle = handleDict[wssMessage[@"sender"]];
        if (jsep==nil && [plugin[@"result"] isEqualToString:@"ok"])
        {
            handle.onJoined(handle);
        }
        else  if (jsep!=nil && [plugin[@"result"] isEqualToString:@"ok"])
        {
            handle.onRemoteJsep(handle, wssMessage[@"jsep"]);
        }
        //                   if (plugin[@"leaving"] != nil)
        //                   {
        //                              JanusHandle *jHandle = feedDict[plugin[@"leaving"]];
        //                              if (jHandle)
        //                              {
        //                                  jHandle.onLeaving(jHandle);
        //                              }
        //                    }
    }else if ([janus isEqualToString:@"error"])
    {
//        NSLog(@"====onMessageerror=%@", wssMessage);
        NSString *transaction = wssMessage[@"transaction"];
        JanusTransaction *jt = transDict[transaction];
        if (jt.error != nil) {
            jt.error(wssMessage);
        }
        [transDict removeObjectForKey:transaction];
    }
    else if ([janus isEqualToString:@"detached"])
    {
        //                      JanusHandle *handle = handleDict[wssMessage[@"sender"]];
        //                      handle.onLeaving(handle);
    }
    else if ([janus isEqualToString:@"hangup"])
    {
//        NSLog(@"hangup:hangup:hangup");
        //                      JanusHandle *handle = handleDict[wssMessage[@"sender"]];
        //                      handle.onLeaving(handle);
    }
    
}


- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error
{
    RTCLogError(@"WebSocket error: %@", error);
    if(error!=nil)
    {
        if ( [BotSi manager].on_error!=nil)
        {
            [BotSi manager].on_error((int)error.code, error.localizedDescription);
        }
    }
   

    NSLog(@"localizedDescription:%@_-------%ld",error.localizedDescription,(long)error.code);
    self.state = kARDSignalingChannelStateError;
}

- (void)webSocket:(SRWebSocket *)webSocket
 didCloseWithCode:(NSInteger)code
           reason:(NSString *)reason
         wasClean:(BOOL)wasClean {
    RTCLog(@"WebSocket closed with code: %ld reason:%@ wasClean:%d",
           (long)code, reason, wasClean);
    NSParameterAssert(_state != kARDSignalingChannelStateError);
    self.state = kARDSignalingChannelStateClosed;
    [keepAliveTimer invalidate];
}

#pragma mark - Private

NSString *letters = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

- (NSString *)randomStringWithLength: (int)len {
    NSMutableString *randomString = [NSMutableString stringWithCapacity: len];
    for (int i = 0; i< len; i++) {
        uint32_t data = arc4random_uniform((uint32_t)[letters length]);
        [randomString appendFormat: @"%C", [letters characterAtIndex: data]];
    }
    return randomString;
}

- (void)createSession
{
    NSString *transaction = [self randomStringWithLength:12];
    
    JanusTransaction *jt = [[JanusTransaction alloc] init];
    jt.tid = transaction;
    jt.success = ^(NSDictionary *data) {
//        NSLog(@"data:%@",data);
        self->sessionId = data[@"data"][@"id"];
       [self->keepAliveTimer fire];
        [self publisherCreateHandle];
    };
    jt.error = ^(NSDictionary *data) {
    };
    transDict[transaction] = jt;
    
    NSDictionary *createMessage = @{
                                    @"janus": @"create",
                                    @"transaction" : transaction,
                                    };
    [_socket send:[self jsonMessage:createMessage]];
}

- (void)publisherCreateHandle
{
    NSString *transaction = [self randomStringWithLength:12];
    JanusTransaction *jt = [[JanusTransaction alloc] init];
    jt.tid = transaction;
    jt.success = ^(NSDictionary *data){
        JanusHandle *handle = [[JanusHandle alloc] init];
        handle.handleId = data[@"data"][@"id"];
//        NSLog(@"handle.handleId:%@", handle.handleId);
        handle.onJoined = ^(JanusHandle *handle) {
            [self.delegate onPublisherJoined: handle.handleId];
        };
        handle.onRemoteJsep = ^(JanusHandle *handle, NSDictionary *jsep) {
            [self.delegate onPublisherRemoteJsep:handle.handleId dict:jsep];
        };
        
        self->handleDict[handle.handleId] = handle;
        [self publisherJoinRoom: handle];
    };
    jt.error = ^(NSDictionary *data) {
    };
    transDict[transaction] = jt;
    
    NSDictionary *attachMessage = @{
                                    @"janus": @"attach",
                                    @"plugin": @"janus.plugin.chattest",
                                    @"transaction": transaction,
                                    @"session_id": sessionId,
                                    };
    [_socket send:[self jsonMessage:attachMessage]];
}

- (void)createHandle: (NSString *) transValue dict:(NSDictionary *)publisher {
}

- (void)publisherJoinRoom : (JanusHandle *)handle {
    
    NSString *transaction = [self randomStringWithLength:12];
    
    NSDictionary *body = @{
                           @"audio": @YES,
                           @"video": @NO,
                           
                           };
    NSDictionary *joinMessage = @{
                                  @"janus": @"message",
                                  @"transaction": transaction,
                                  @"session_id":sessionId,
                                  @"handle_id":handle.handleId,
                                  @"body": body
                                  };
    
    [_socket send:[self jsonMessage:joinMessage]];
    
}

- (void)publisherCreateOffer:(NSNumber *)handleId sdp: (RTCSessionDescription *)sdp {
    NSString *transaction = [self randomStringWithLength:12];
    
    NSDictionary *publish = @{
                              @"audio": @YES,
                              @"video": @NO,
                              };
//    NSLog(@"publisherCreateOffer");
    NSString *type = [RTCSessionDescription stringForType:sdp.type];
    
    NSDictionary *jsep = @{
                           @"type": type,
                           @"sdp": [sdp sdp],
                           };
    NSDictionary *offerMessage = @{
                                   @"janus": @"message",
                                   @"body": publish,
                                   @"jsep": jsep,
                                   @"transaction": transaction,
                                   @"session_id": sessionId,
                                   @"handle_id": handleId,
                                   };
    
    
    [_socket send:[self jsonMessage:offerMessage]];
}

- (void)trickleCandidate:(NSNumber *) handleId candidate: (RTCIceCandidate *)candidate {
    NSDictionary *candidateDict = @{
                                    //                                @"ios": @"true",
                                    @"candidate": candidate.sdp,
                                    @"sdpMid": candidate.sdpMid,
                                    @"sdpMLineIndex": [NSNumber numberWithInt: candidate.sdpMLineIndex],
                                    };
    
    NSDictionary *trickleMessage = @{
                                     @"janus": @"trickle",
                                     //                                     @"ios": @"true",
                                     @"candidate": candidateDict,
                                     @"transaction": [self randomStringWithLength:12],
                                     @"session_id":sessionId,
                                     @"handle_id":handleId,
                                     };
    
    //    NSLog(@"===trickle==%@", trickleMessage);
    [_socket send:[self jsonMessage:trickleMessage]];
}

- (void)trickleCandidateComplete:(NSNumber *) handleId {
    NSDictionary *candidateDict = @{
                                    @"completed": @YES,
                                    };
    NSDictionary *trickleMessage = @{
                                     @"janus": @"trickle",
                                     //                                      @"ios": @"true",
                                     @"candidate": candidateDict,
                                     @"transaction": [self randomStringWithLength:12],
                                     @"session_id":sessionId,
                                     @"handle_id":handleId,
                                     };
    
    [_socket send:[self jsonMessage:trickleMessage]];
}


- (void)subscriberCreateHandle: (NSNumber *)feed display:(NSString *)display handle:(JanusHandle*)_handle {
    
    //    NSString *transaction = [self randomStringWithLength:12];
    //    JanusTransaction *jt = [[JanusTransaction alloc] init];
    //    jt.tid = transaction;
    //    jt.success = ^(NSDictionary *data){
    ////        NSLog(@"data:%@",data);
    ////        JanusHandle *handle = [[JanusHandle alloc] init];
    ////        handle.handleId = sessionId;//data[@"data"][@"id"];
    ////        handle.feedId = feed;
    ////        handle.display = display;
    ////        NSLog(@"%@,%@,%@",handle.handleId,_handle.handleId,sessionId);
    //
    //        _handle.onRemoteJsep = ^(JanusHandle *handle, NSDictionary *jsep) {
    //            [self.delegate subscriberHandleRemoteJsep:handle.handleId dict:jsep];
    //        };
    //
    ////        handle.onLeaving = ^(JanusHandle *handle) {
    ////            [self subscriberOnLeaving:handle];
    ////        };
    ////        handleDict[handle.handleId] = handle;
    ////        feedDict[handle.feedId] = handle;
    ////        [self subscriberJoinRoom: handle];
    //    };
    //    jt.error = ^(NSDictionary *data) {
    //    };
    //    transDict[transaction] = jt;
    //
    //    NSDictionary *body = @{
    //                           @"request": @"join",
    //                           @"room": @1234,
    //                           @"ptype": @"listener",
    //                           @"feed": feed,
    //                           };
    //
    //    NSDictionary *message = @{
    //                              @"janus": @"message",
    //                              @"transaction": transaction,
    //                              @"session_id": sessionId,
    //                              @"handle_id": _handle.handleId,
    ////                               @"ios": @"true",
    //                              @"body": body,
    //                              };
    //    NSDictionary *attachMessage = @{
    //                                    @"janus": @"attach",
    //                                    @"plugin": @"janus.plugin.echotest",
    //                                    @"transaction": transaction,
    //                                    @"session_id": sessionId,
    //                                    };
    //    NSLog(@"message:%@",message);
    //    [_socket send:[self jsonMessage:message]];
}


- (void)subscriberJoinRoom:(JanusHandle*)handle {
    
    //    NSString *transaction = [self randomStringWithLength:12];
    //    transDict[transaction] = @"subscriber";
    ////    NSLog(@"handle.feedId:%@",handle.feedId);
    //    NSDictionary *body = @{
    //                           @"request": @"join",
    //                           @"room": @1234,
    //                           @"ptype": @"listener",
    //                           @"feed": handle.feedId,
    //                           };
    //
    //    NSDictionary *message = @{
    //                                  @"janus": @"message",
    //                                  @"transaction": transaction,
    //                                  @"session_id": sessionId,
    //                                  @"handle_id": handle.handleId,
    //                                  @"body": body,
    //                                  };
    //
    //    [_socket send:[self jsonMessage:message]];
}

- (void)subscriberCreateAnswer:(NSNumber *)handleId sdp: (RTCSessionDescription *)sdp  {
    //    NSString *transaction = [self randomStringWithLength:12];
    //
    //    NSDictionary *body = @{
    //                              @"request": @"start",
    //                              @"room": @1234,
    //                              };
    //
    //    NSString *type = [RTCSessionDescription stringForType:sdp.type];
    //
    //    NSDictionary *jsep = @{
    //                           @"type": type,
    //                           @"sdp": [sdp sdp],
    //                           };
    //    NSDictionary *offerMessage = @{
    //                                   @"janus": @"message",
    //                                   @"body": body,
    //                                   @"jsep": jsep,
    //                                   @"transaction": transaction,
    //                                   @"session_id": sessionId,
    //                                   @"handle_id": handleId,
    //                                   };
    //
    //    [_socket send:[self jsonMessage:offerMessage]];
}

- (void)subscriberOnLeaving:(NSNumber *) handleId
{
//      NSLog(@"detachdetachdetachdetachdetachdetach");
        NSString *transaction = [self randomStringWithLength:12];

        JanusTransaction *jt = [[JanusTransaction alloc] init];
        jt.tid = transaction;
        jt.success = ^(NSDictionary *data) {
            [self.delegate onLeaving:handleId];
            [self->handleDict removeObjectForKey:handleId];
    //        [self->feedDict removeObjectForKey:handle.feedId];
        };
        jt.error = ^(NSDictionary *data)
        {
            [self.delegate onLeaving:handleId];
            [self->handleDict removeObjectForKey:handleId];
            NSDictionary * errdic=[data objectForKey:@"error"];
            if (errdic!=nil) {
                NSString * codestr=[errdic objectForKey:@"code"];
                NSString * reason=[errdic objectForKey:@"reason"];
                if (codestr!=nil) {
                    int code=[codestr intValue];
                    if ([BotSi manager].on_error!=nil)
                    {
                        [BotSi manager].on_error(code, reason);
                    }
                }

            }
        };
        transDict[transaction] = jt;

        NSDictionary *message = @{
                                       @"janus": @"detach",
                                       @"transaction": transaction,
                                       @"session_id": sessionId,
                                       @"handle_id": handleId,
                                       };

        [_socket send:[self jsonMessage:message]];
}

- (void)keepAlive
{
//     NSLog(@"sessiond:%@",sessionId);
    if (sessionId!=nil)
    {
        NSDictionary *dict = @{
                               @"janus": @"keepalive",
                               @"session_id": sessionId,
                               @"transaction": [self randomStringWithLength:12],
                               };
        [_socket send:[self jsonMessage:dict]];
    }
    
}

- (NSString *)jsonMessage:(NSDictionary *)dict
{
    NSData *message = [NSJSONSerialization dataWithJSONObject:dict
                                                      options:NSJSONWritingPrettyPrinted
                                                        error:nil];
    NSString *messageString = [[NSString alloc] initWithData:message encoding:NSUTF8StringEncoding];
    return messageString;
}


@end


